package com.jiayou.config.vertx

import cn.hutool.core.text.StrBuilder
import com.baomidou.mybatisplus.core.override.MybatisMapperProxy
import com.jiayou.VertxWebApplication
import com.jiayou.config.vertx.handler.AnnotationBaseHandler
import com.jiayou.config.vertx.handler.VertBaseHandler
import com.jiayou.config.vertx.handler.httpstate.HttpStateHandler
import com.jiayou.config.vertx.handler.jwt.JwtAuthenticationHandler
import com.jiayou.config.vertx.handler.jwt.JwtHandler
import com.jiayou.config.vertx.handler.jwt.JwtPermissionsHandler
import io.vertx.core.AbstractVerticle
import io.vertx.core.http.HttpMethod
import io.vertx.core.http.HttpServer
import io.vertx.core.impl.ConcurrentHashSet
import io.vertx.core.impl.launcher.commands.VersionCommand
import io.vertx.ext.auth.KeyStoreOptions
import io.vertx.ext.auth.jwt.JWTAuth
import io.vertx.ext.auth.jwt.JWTAuthOptions
import io.vertx.ext.web.Route
import io.vertx.ext.web.Router
import io.vertx.ext.web.handler.BodyHandler
import io.vertx.ext.web.handler.CorsHandler
import io.vertx.ext.web.handler.LoggerHandler
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.ApplicationContext
import org.springframework.stereotype.Component
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import javax.annotation.Resource


/**
@author lishuang
@data   2021-06-25  17:10:37
@info   vertx核心
 */

@Component
class VertxApplication : AbstractVerticle() {
    companion object {


        @JvmStatic
        private val logger = LoggerFactory.getLogger(VertxApplication::class.java)

        @JvmStatic
        val filtrationURL = ConcurrentHashSet<String>()

        init {
            filtrationURL.add("/login")
            filtrationURL.add("/*")
        }

        @JvmStatic
        val handlers = ConcurrentHashMap<String, VertBaseHandler>()

    }


    lateinit var server: HttpServer
    lateinit var router: Router
    private lateinit var jwtAuth: JWTAuth

    @Autowired
    private lateinit var springApplication: ApplicationContext

    @Autowired
    private lateinit var jwtPermissionsHandler: JwtPermissionsHandler

    @Value("${'$'}{jwt.keyPath}")
    lateinit var keyPath: String

    @Value("${'$'}{server.port}")
    var port: Int = 8080


    override fun start() {

        server = vertx.createHttpServer()
        router = Router.router(vertx)
        jwtAuth = JWTAuth.create(vertx, initJwt())
        router.route().handler(BodyHandler.create())
        router.route().handler(LoggerHandler.create())

        initCors()

        router.get("/login").handler(JwtAuthenticationHandler(jwtAuth))
        router.route().handler(JwtHandler(jwtAuth))
        router.route().handler(jwtPermissionsHandler)


        registerRoute()

        HttpStateHandler.errorHandle(HttpStateHandler.STATUS, router)

        server.requestHandler(router).listen(port)
                .onSuccess {
                    logger.info("vertx-web started in port $port !")
                }
                .onFailure {
                    logger.info("vertx-web started failed !")
                    it.printStackTrace()
                }
    }

    private fun initCors(): Route {
        val allowedHeaders = HashSet<String>()
        val allowedMethods = HashSet<HttpMethod>()

        allowedHeaders.add("x-requested-with")
        allowedHeaders.add("Access-Control-Allow-Origin")
        allowedHeaders.add("origin")
        allowedHeaders.add("Content-Type")
        allowedHeaders.add("accept")
        allowedHeaders.add("X-PINGARUNER")

        allowedMethods.add(HttpMethod.GET)
        allowedMethods.add(HttpMethod.POST)
        allowedMethods.add(HttpMethod.PUT)
        allowedMethods.add(HttpMethod.DELETE)
        allowedMethods.add(HttpMethod.OPTIONS)
        allowedMethods.add(HttpMethod.PATCH)

        return router.route().handler(CorsHandler.create("*").allowedHeaders(allowedHeaders).allowedMethods(allowedMethods))
    }

    private fun initJwt(): JWTAuthOptions {
        return JWTAuthOptions()
                .setKeyStore(KeyStoreOptions()
                        .setType("jceks")
                        .setPath(keyPath)
                        .setPassword("secret"))
    }

    private fun registerRoute() {
        springApplication.getBeansOfType(VertBaseHandler::class.java).entries.forEach {
            try {
                if (it.value is VertBaseHandler) {
                    handlers[it.key] = it.value
                    it.value.init(this)
                    it.value.handle(this)
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        this.printBanner()
    }

    private fun printBanner() {
        val width = 100
        val banner = StrBuilder()
        banner.append("                   _            \n" +
                "                  | |           \n" +
                "__   __ ___  _ __ | |_    __  __\n" +
                "\\ \\ / // _ \\| '__|| __|   \\ \\/ /\n" +
                " \\ V /|  __/| |   | |_  _  >  < \n" +
                "  \\_/  \\___||_|    \\__|(_)/_/\\_\\\n" +
                "                             ${VersionCommand.getVersion()}           \n")
        if (AnnotationBaseHandler.deployInfo.size > 0) {
            val headInfo = StrBuilder()
            for (i in 1..width / 3) headInfo.append(" ")
            headInfo.append("\t\t   Vertx  deploy  info  \t\t")
            headInfo.append("\t")
            banner.append(headInfo)
            banner.append("\n")
            for (i in 1..width) banner.append("*")
            banner.append("\n")
            AnnotationBaseHandler.deployInfo.forEach {
                val deployInfo = StrBuilder()
                deployInfo.append("--->\t$it")
                deployInfo.append("\n")
                banner.append(deployInfo)
            }
            for (i in 1..width) banner.append("*")
            banner.append("\n")
        }
        println(banner.toString())
    }

}